﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace FloralTerrainGenerator.Source {
	class Terrain {

		VertexPositionNormalTexture[] vertices;
		int[] triangleVertices;

		int width, depth;
		int lowest, heighest;
		int cellsWide, cellsDeep, totalCells;
		int cellWidth, cellDepth;
		int totalFaces;
		int numVerticesX, numVerticesZ, totalNumVertices;

		float terrainStartX, terrainStartZ;

		BasicEffect effect;
		Boolean wf = false;

		Texture2D grass;

		public Terrain(int pWidth, int pDepth, int pVertsWide, int pVertsDeep, int pLowest, int pHeighest, String heightmap) {
			//FloralTerrainGenerator.gd.RenderState.FillMode = FillMode.WireFrame;

			width = pWidth;
			depth = pDepth;
			cellsWide = pVertsWide-1;
			cellsDeep = pVertsDeep-1;
			lowest = pLowest;
			heighest = pHeighest;

			effect = new BasicEffect(FloralTerrainGenerator.gd, null);

			effect.World = Matrix.Identity;
			effect.View = Matrix.CreateLookAt(new Vector3(0, 100, 100), Vector3.Zero, Vector3.Up);
			float ar = GraphicsDeviceManager.DefaultBackBufferWidth/GraphicsDeviceManager.DefaultBackBufferHeight;
			effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, ar, 1, 1000);

			createGrid(pWidth, pDepth, cellsWide, cellsDeep);

			//heightmap stuff
			Texture2D hM = FloralTerrainGenerator.sContent.Load<Texture2D>(heightmap);
			float[,] heights = new float[numVerticesX, numVerticesZ];

			heights = getColorData(hM);

			//compress color data
			for(int a=0;a<hM.Height;a++) {
				for(int b=0;b<hM.Width;b++) {
					float height = heights[a, b];
					height = height/255;
					height *= (heighest-lowest);
					height += lowest;
					heights[a, b] = height;
				}
			}

			//assign heights
			int stepper = 0;
			int zStep = hM.Height/numVerticesZ;
			int xStep = hM.Width/numVerticesX;
			//Console.WriteLine(xStep+" "+zStep);
			for(int a=0; a<hM.Height; a += zStep) {
				for(int b=0; b<hM.Width; b += xStep) {
					vertices[stepper].Position.Y = heights[a, b];
					stepper++;
				}
			}

			//assign normals
			for(int a=0;a<triangleVertices.Length;a+=3) {
				VertexPositionNormalTexture vert1, vert2, vert3;
				vert1 = vertices[triangleVertices[a]];
				vert2 = vertices[triangleVertices[a+1]];
				vert3 = vertices[triangleVertices[a+2]];

				Vector3 vec1 = new Vector3(vert2.Position.X - vert1.Position.X, vert2.Position.Y - vert1.Position.Y, vert2.Position.Z - vert1.Position.Z);
				Vector3 vec2 = new Vector3(vert3.Position.X - vert1.Position.X, vert3.Position.Y - vert1.Position.Y, vert3.Position.Z - vert1.Position.Z);

				Vector3 norm = Vector3.Cross(vec2, vec1);

				vertices[triangleVertices[a]].Normal += norm;
				vertices[triangleVertices[a+1]].Normal += norm;
				vertices[triangleVertices[a+2]].Normal += norm;
			}

			for(int a=0;a<vertices.Length;a++) {
				vertices[a].Normal.Normalize();
			}

			//textures coords
			stepper = 0;
			for(int a=0; a<numVerticesZ; a++) {
				for(int b=0; b<numVerticesX; b++) {
					vertices[stepper].TextureCoordinate.X = b; // (float)b/(numVerticesX-1);
					vertices[stepper].TextureCoordinate.Y = a; // (float)a/(numVerticesZ-1);
					Console.Write(vertices[stepper].TextureCoordinate + " ");
					stepper++;
				}
				Console.WriteLine();
			}
			Console.WriteLine();
			for(int a=0;a<vertices.Length;a++) {
				Console.Write(vertices[a].TextureCoordinate);
			}

			//texture testing
			grass = FloralTerrainGenerator.sContent.Load<Texture2D>("textures//grass");
			effect.Texture = grass;
			effect.TextureEnabled = true;

			/*for(int a=0; a<triangleVertices.Length; a+=6) {
				int vert1 = triangleVertices[a];
				int vert2 = triangleVertices[a+1];
				int vert3 = triangleVertices[a+2];

				vertices[vert1].TextureCoordinate.X = 0;
				vertices[vert1].TextureCoordinate.Y = 0;
				vertices[vert2].TextureCoordinate.X = 1;
				vertices[vert2].TextureCoordinate.Y = 0;
				vertices[vert3].TextureCoordinate.X = 0;
				vertices[vert3].TextureCoordinate.Y = 1;

				vert1 = triangleVertices[a+3];
				vert2 = triangleVertices[a+4];
				vert3 = triangleVertices[a+5];

				vertices[vert1].TextureCoordinate.X = 1;
				vertices[vert1].TextureCoordinate.Y = 0;
				vertices[vert2].TextureCoordinate.X = 1;
				vertices[vert2].TextureCoordinate.Y = 1;
				vertices[vert3].TextureCoordinate.X = 0;
				vertices[vert3].TextureCoordinate.Y = 1;
			}*/

			FloralTerrainGenerator.gd.VertexDeclaration = new VertexDeclaration(FloralTerrainGenerator.gd, VertexPositionNormalTexture.VertexElements);
		}

		private float[,] getColorData(Texture2D image) {
			Color[] colors = new Color[image.Height * image.Width];
			image.GetData(colors);
			Color[,] colors2D = new Color[image.Height, image.Width];

			int stepper = 0;
			for(int a=0;a<image.Height;a++) {
				for(int b=0;b<image.Width;b++) {
					colors2D[a, b] = colors[stepper];
					stepper++;
				}
			}

			float[,] bwData = new float[image.Height, image.Width];
			for(int a=0;a<image.Height;a++) {
				for(int b=0;b<image.Width;b++) {
					bwData[a, b] = (colors2D[a, b].R + colors2D[a, b].G + colors2D[a, b].B)/3;
				}
			}

			return bwData;

		}

		public void createGrid(int pWidth, int pDepth, int pCellsWide, int pCellsDeep) {
			totalCells = cellsDeep * cellsWide;
			cellWidth = width/cellsWide;
			cellDepth = depth/cellsDeep;
			numVerticesX = cellsWide + 1;
			numVerticesZ = cellsDeep + 1;
			totalNumVertices = numVerticesX * numVerticesZ;
			totalFaces = totalCells * 2;

			terrainStartX = -width/2;
			terrainStartZ = -depth/2;

			vertices = new VertexPositionNormalTexture[totalNumVertices];

			triangleVertices = new int[totalFaces*3];
			int counter = 0;
			for(int a=0;a<numVerticesZ;a++) {
				for(int b=0;b<numVerticesX;b++) {
					vertices[counter] = new VertexPositionNormalTexture();
					vertices[counter].Position = new Vector3(terrainStartX + cellWidth*b, 0, terrainStartZ + cellDepth*a);
					vertices[counter].Normal = Vector3.Zero;
					counter++;
				}
			}

			int startVertex = 0;
			int index = 0;
			for(int i=0;i<cellsDeep;i++) {
				for(int j=0;j<cellsWide;j++) {
					triangleVertices[index] = startVertex;
					triangleVertices[index+1] = startVertex + 1;
					triangleVertices[index+2] = startVertex + numVerticesX;

					triangleVertices[index+3] = startVertex+1;
					triangleVertices[index+4] = startVertex + numVerticesX + 1;
					triangleVertices[index+5] = startVertex + numVerticesX;

					index += 6;
					startVertex++;
				}
				startVertex++;
			}
		}

		public void moveTerrain(Vector3 mo) {
			for(int a=0; a<vertices.Length; a++) {
				vertices[a].Position += mo;
			}
		}

		public void isWireFrame(bool wf) {
			this.wf = wf;
		}

		public void Draw(SpriteBatch spriteBatch) {
			if(wf) {
				FloralTerrainGenerator.gd.RenderState.FillMode = FillMode.WireFrame;
			} else {
				FloralTerrainGenerator.gd.RenderState.FillMode = FillMode.Solid;
			}

			effect.Begin();
			foreach(EffectPass pass in effect.CurrentTechnique.Passes) {
				pass.Begin();
				FloralTerrainGenerator.gd.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertices, 0, vertices.Length, triangleVertices, 0, triangleVertices.Length/3);
				pass.End();
			}
			effect.End();
		}
	}
}
